package com.hurried1y.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hurried1y.exception.SystemException;
import com.hurried1y.model.constant.RedisKeyConstant;
import com.hurried1y.model.entity.UserWalletDetail;
import com.hurried1y.model.enums.WalletSourceEnum;
import com.hurried1y.model.enums.WalletTypeEnum;
import com.hurried1y.model.vo.UserConsumeVo;
import com.hurried1y.model.vo.UserWalletVo;
import com.hurried1y.service.UserWalletDetailService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.hurried1y.mapper.UserWalletMapper;
import com.hurried1y.model.entity.UserWallet;
import com.hurried1y.service.UserWalletService;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * (UserWallet)表服务实现类
 *
 * @author Hurried1y
 * @since 2023-06-19 11:09:20
 */
@Service
@Slf4j
public class UserWalletServiceImpl extends ServiceImpl<UserWalletMapper, UserWallet> implements UserWalletService {
    @Autowired
    private UserWalletDetailService userWalletDetailService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public UserWalletVo getUserWallet(Long userId) {
        log.info("查询用户钱包余额，userId:{}", userId);

        LambdaQueryWrapper<UserWallet> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserWallet::getUserId, userId);
        UserWallet wallet = getOne(wrapper);
        if(wallet == null){
            log.info("用户钱包不存在，userId:{}", userId);
            return null;
        }
        log.info("查询用户 userId:{} 钱包余额完成，数据为:{}", userId, wallet);
        UserWalletVo userWalletVo = new UserWalletVo();
        userWalletVo.setUserId(wallet.getUserId());
        userWalletVo.setTotalAmount(wallet.getTotalAmount());
        return userWalletVo;
    }

    @Transactional
    @Override
    public void consume(UserConsumeVo consumeVo) {
        //幂等性校验
        idempotentWithRedis(consumeVo);

        LambdaQueryWrapper<UserWallet> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserWallet::getUserId, consumeVo.getUserId());
        UserWallet wallet = getOne(wrapper);
        //用户更新钱包操作 -- 分布式锁
        RLock lock = redissonClient.getLock(RedisKeyConstant.UPDATE_WALLET_LOCK_KEY);
        lock.lock(RedisKeyConstant.EXPIRE_TIME, TimeUnit.MILLISECONDS);

        log.info("用户钱包更新，userId:{}, wallet:{}", consumeVo.getUserId(), wallet);

        UserWallet updateWallet = new UserWallet();

        Integer updateType = consumeVo.getUpdateType();
        Integer updateSource = consumeVo.getUpdateSource();

        //获取更新后的钱包
        getUpdateWallet(updateType, updateSource, updateWallet, wallet, consumeVo);

        updateWallet.setWalletId(wallet.getWalletId());
        updateById(updateWallet);

        log.info("用户钱包更新完成，userId:{}, wallet:{}", consumeVo.getUserId(), wallet);

        //记录钱包变动明细
        UserWalletDetail detail = new UserWalletDetail();
        detail.setDetailId(IdWorker.getId());
        detail.setUserId(consumeVo.getUserId());
        detail.setBankAccount(consumeVo.getBankAccount());
        detail.setUpdateAmount(consumeVo.getUpdateAmount());
        detail.setUpdateType(updateType);
        detail.setUpdateSource(updateSource);
        detail.setOrderId(consumeVo.getOrderId());
        detail.setBeforeAmount(wallet.getTotalAmount());
        detail.setAfterAmount(updateWallet.getTotalAmount());
        userWalletDetailService.save(detail);

        log.info("用户钱包明细记录完成，detail:{}", detail);

        lock.unlock();
    }

    private void idempotentWithRedis(UserConsumeVo consumeVo) {
        //计算hash
        int hashCode = consumeVo.hashCode();
        String value = String.valueOf(hashCode);

        log.info("幂等性校验，hashCode:{}, value:{}", hashCode, value);
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(RedisKeyConstant.CONSUME_IDEMPOTENT_KEY, value, RedisKeyConstant.CONSUME_IDEMPOTENT_EXPIRE_TIME, TimeUnit.MILLISECONDS);
        if(!aBoolean){
            log.info("幂等性校验失败，hashCode:{}, value:{}", hashCode, value);
            throw new SystemException(500, "请勿重复提交");
        }

        log.info("幂等性校验成功，hashCode:{}, value:{}", hashCode, value);
    }

    private void getUpdateWallet(Integer updateType, Integer updateSource, UserWallet updateWallet, UserWallet wallet, UserConsumeVo consumeVo) {
        if(updateType.equals(WalletTypeEnum.EXPEND.getCode())) {
            //更新类型为支出
            if(updateSource.equals(WalletSourceEnum.SHOPPING.getCode())){
                //更新来源为购物
                log.info("用户购物，支出金额:{}", consumeVo.getUpdateAmount());
            }
            else if(updateSource.equals(WalletSourceEnum.WITHDRAW.getCode())){
                //更新来源为提现
                log.info("用户提现，支出金额:{}", consumeVo.getUpdateAmount());
            }
            updateWallet.setTotalAmount(wallet.getTotalAmount().subtract(consumeVo.getUpdateAmount()));
        } else {
            //更新类型为收入
            if(updateSource.equals(WalletSourceEnum.RECHARGE.getCode())){
                //更新来源为充值
                log.info("用户充值，收入金额:{}", consumeVo.getUpdateAmount());
            }
            else if(updateSource.equals(WalletSourceEnum.REFUND.getCode())){
                //更新来源为退款
                log.info("用户退款，收入金额:{}", consumeVo.getUpdateAmount());
            }
            updateWallet.setTotalAmount(wallet.getTotalAmount().add(consumeVo.getUpdateAmount()));
        }
    }
}

